import time
import copy
from types import NoneType
from xml.dom.minidom import Document
from decimal import Decimal

CP_NS = "http://services.tnc.org/schema/conservation-project/9"
ENCODING = 'utf-8'
CONTACT_PERSON_ROLES_ORDERED = ['Team Contact','Leader/Manager','Team Leader','Process Facilitator','Team Member','Project Advisor','Stakeholder']


class XmlTearDown:

    def __init__(self):
        self.cp_data_structure = {}
        self.tear_down_errors = []
        self.error_msg_list = []

    def extract_indicator_info(self,indicator_nodes):
        """Extract indicator info from indicators_node It contains 0 to many indicators.
        """
        indicators = []
        for current_indicator_node in indicator_nodes:
            id = current_indicator_node.getAttribute('id')
            name = ' '.join(return_xml_data(current_indicator_node.getElementsByTagNameNS(CP_NS,'name')[0]).split())
            # factor_type and factor_name are not currently being exported by Miradi (version 3.0)
            try:
                factor_type = return_xml_data(current_indicator_node.getElementsByTagNameNS(CP_NS,'factor_type')[0])
            except:
                factor_type = u''
            try:
                factor_name = return_xml_data(current_indicator_node.getElementsByTagNameNS(CP_NS,'factor_name')[0])
            except:
                factor_name = u''
            try:
                priority = return_xml_data(current_indicator_node.getElementsByTagNameNS(CP_NS,'priority')[0])
            except:
                priority = u''
            try:
                comment = return_xml_data(current_indicator_node.getElementsByTagNameNS(CP_NS,'comment')[0])
            except:
                comment = u''
            measures_node = current_indicator_node.getElementsByTagNameNS(CP_NS, 'measures')[0]
            measure_nodes = measures_node.getElementsByTagNameNS(CP_NS, 'measure')
            measures = self.extract_measures_info(measure_nodes)
            method_ids = []
            methods_node = current_indicator_node.getElementsByTagNameNS(CP_NS, 'methods')[0]
            method_id_nodes = methods_node.getElementsByTagNameNS(CP_NS, 'method_id')
            for current_method_id_node in method_id_nodes:
                if current_method_id_node.hasChildNodes():
                    method_id = return_xml_data(current_method_id_node)
                    method_ids.append(method_id)
            status_nodes = current_indicator_node.getElementsByTagNameNS(CP_NS, 'status')
            statuses = self.extract_status_info(status_nodes)

            indicators.append({ 'id':           id,
                                'name':         name,
                                'factor_type':  factor_type,
                                'factor_name':  factor_name,
                                'priority':     priority,
                                'comment':      comment,
                                'measures':     measures,
                                'method_ids':   method_ids,
                                'statuses':     statuses,
                                })
        return indicators


    def extract_measures_info (self,measure_nodes):
        """
        Extract measures info from measure_nodes. It contains 0 to many measures.
        """
        measures = []
        for current_measure_node in measure_nodes:
            sequence = current_measure_node.getAttribute('sequence')
            try:
                measurement = return_xml_data(current_measure_node.getElementsByTagNameNS(CP_NS,'measurement')[0])
            except:
                measurement = u''
            try:
                comment = return_xml_data(current_measure_node.getElementsByTagNameNS(CP_NS,'comment')[0])
            except:
                comment = u''
            try:
                date = return_xml_data(current_measure_node.getElementsByTagNameNS(CP_NS,'date')[0])
            except:
                date = u''
            try:
                source = return_xml_data(current_measure_node.getElementsByTagNameNS(CP_NS,'source')[0])
            except:
                source = u''
            try:
                trend = return_xml_data(current_measure_node.getElementsByTagNameNS(CP_NS,'trend')[0])
            except:
                trend = u''
            try:
                rating = return_xml_data(current_measure_node.getElementsByTagNameNS(CP_NS,'rating')[0])
            except:
                rating = u''

            measures.append({   'sequence':     sequence,
                                'measurement':  measurement,
                                'comment':      comment,
                                'date':         fix_date(date),
                                'source':       source,
                                'trend':        trend,
                                'rating':       rating,
                                })
        return measures


    def extract_method_info (self,method_nodes):
        """
        Extract methods info from method_nodes. It contains 0 to many methods.
        """
        methods = []
        for current_method_node in method_nodes:
            id = current_method_node.getAttribute('id')
            name = ' '.join(return_xml_data(current_method_node.getElementsByTagNameNS(CP_NS,'name')[0]).split())
            try:
                detail = return_xml_data(current_method_node.getElementsByTagNameNS(CP_NS,'detail')[0])
            except:
                detail = u''
            try:
                comment = return_xml_data(current_method_node.getElementsByTagNameNS(CP_NS,'comment')[0])
            except:
                comment = u''

            methods.append({   'id':           id,
                                'name':         name,
                                'detail':       detail,
                                'comment':      comment,
                                })
        return methods


    def extract_strategy_info (self,strategies_node):
        """
        Extract strategy info from strategies_node. It contains 0 to many strategies.
        """
        strategies = []
        for strategy_node in strategies_node:
            # Get all nodes named 'strategy'
            strategy_nodes = strategy_node.getElementsByTagNameNS(CP_NS, 'strategy')
            for current_strategy_node in strategy_nodes:
                id = current_strategy_node.getAttribute('id')
                name = ' '.join(return_xml_data(current_strategy_node.getElementsByTagNameNS(CP_NS,'name')[0]).split())
                strategy_objective_nodes = current_strategy_node.getElementsByTagNameNS(CP_NS, 'objectives')
                for current_strategy_objective_node in strategy_objective_nodes:
                    objective_ids = []
                    # Get Objective ID Nodes from strategy_objective_nodes
                    strategy_objective_id_nodes = current_strategy_objective_node.getElementsByTagNameNS(CP_NS, 'objective_id')
                    for obj_id in strategy_objective_id_nodes:
                        try:
                            objective_id = return_xml_data(obj_id)
                            objective_ids.append(objective_id)
                        except:
                            pass # no objective_ids
                try:
                    taxonomy_code = return_xml_data(current_strategy_node.getElementsByTagNameNS(CP_NS,'taxonomy_code')[0])
                except:
                    taxonomy_code = u'0'
                try:
                    selected = return_xml_data(current_strategy_node.getElementsByTagNameNS(CP_NS,'selected')[0])
                except:
                    selected = u''
                if selected != u'':
                    if not isinstance(selected,basestring):
                        selected = u'%s' % (selected)
                    if isinstance(selected,basestring):
                        if selected.lower() == 'true':
                            selected = 'YES'
                        elif selected.lower() == 'false':
                            selected = 'NO'
                        elif selected.lower() == '0':
                            selected = 'NO'
                        elif selected.lower() == '1':
                            selected = 'YES'
                        else:
                            selected = 'NO'
                else:
                    selected = 'NO'
                try:
                    comment = return_xml_data(current_strategy_node.getElementsByTagNameNS(CP_NS,'comment')[0])
                except:
                    comment = u''
                try:
                    total_cost = return_xml_data(current_strategy_node.getElementsByTagNameNS(CP_NS,'total_cost')[0])
                except:
                    total_cost = u''

                # NOTE: Extract the strategy activities before the strategy statuses.  This allows us to parse the activity statuses and remove them
                # from the local DOM before we get to the strategy statuses.  Otherwise referencing 
                # current_strategy_activity_node.getElementsByTagNameNS(CP_NS, 'status') would not only extract the strategy statuses but also
                # the activity statuses.
                strategy_activities_nodes = current_strategy_node.getElementsByTagNameNS(CP_NS, 'activities')
                activities = []
                for strategy_activity_node in strategy_activities_nodes:
                    if strategy_activity_node.hasChildNodes():
                        for current_strategy_activity_node in strategy_activity_node.childNodes:
                            try:
                                activity_name = ' '.join(return_xml_data(current_strategy_activity_node.getElementsByTagNameNS(CP_NS,'name')[0]).split())
                                try:
                                    start_date = return_xml_data(current_strategy_activity_node.getElementsByTagNameNS(CP_NS,'start_date')[0])
                                except:
                                    start_date = u''
                                try:
                                    end_date = return_xml_data(current_strategy_activity_node.getElementsByTagNameNS(CP_NS,'end_date')[0])
                                except:
                                    end_date = u''
                                try:
                                    activity_total_cost = return_xml_data(current_strategy_activity_node.getElementsByTagNameNS(CP_NS,'total_cost')[0])
                                except:
                                    activity_total_cost = u''
                                try:
                                    detail = return_xml_data(current_strategy_activity_node.getElementsByTagNameNS(CP_NS,'detail')[0])
                                except:
                                    detail = u''
                                activity_status_nodes = current_strategy_activity_node.getElementsByTagNameNS(CP_NS, 'status')
                                activity_statuses = self.extract_status_info(activity_status_nodes)
                                # Now remove the activity status nodes - see comments above.
                                try:
                                    statuses_node = current_strategy_activity_node.getElementsByTagNameNS(CP_NS, 'statuses')[0]
                                    statuses_node.parentNode.removeChild(statuses_node)
                                    statuses_node.unlink()
                                except:
                                    pass
                                activities.append({ 'name':         activity_name,
                                                    'start_date':   fix_date(start_date),
                                                    'end_date':     fix_date(end_date),
                                                    'total_cost':   activity_total_cost,
                                                    'detail':       detail,
                                                    'statuses':     activity_statuses,
                                                    })
                            except:
                                pass # no activities

                strategy_status_nodes = current_strategy_node.getElementsByTagNameNS(CP_NS, 'status')
                strategy_statuses = self.extract_status_info(strategy_status_nodes)

                strategies.append({ 'id':                       id,
                                    'name':                     name,
                                    'objective_ids':            objective_ids,
                                    'taxonomy_code':            taxonomy_code,
                                    'selected':                 selected,
                                    'comment':                  comment,
                                    'total_cost':               total_cost,
                                    'statuses':                 strategy_statuses,
                                    'activities':               activities,
                                    })
        return strategies


    def extract_status_info (self,status_nodes):
        """
        Extract status info from status_nodes. It contains 0 to many statuses.
        """
        statuses = []
        for current_status_node in status_nodes:
            sequence = current_status_node.getAttribute('sequence')
            try:
                date = return_xml_data(current_status_node.getElementsByTagNameNS(CP_NS,'date')[0])
            except:
                date = u''
            try:
                measure = return_xml_data(current_status_node.getElementsByTagNameNS(CP_NS,'measure')[0])
            except:
                measure = u''
            try:
                comment = return_xml_data(current_status_node.getElementsByTagNameNS(CP_NS,'comment')[0])
            except:
                comment = u''

            statuses.append({  'sequence':     sequence,
                               'measure':      measure,
                               'date':         fix_date(date),
                               'comment':      comment,
                               })
        return statuses


    def extract_objective_info (self,objective_nodes):
        """
        Extract objective info from objectives_node. It contains 0 to many objectives.
        """
        objectives = []
        for current_objective_node in objective_nodes:
            id = current_objective_node.getAttribute('id')
            name = ' '.join(return_xml_data(current_objective_node.getElementsByTagNameNS(CP_NS,'name')[0]).split())
            indicator_ids = []
            objective_indicators_node = current_objective_node.getElementsByTagNameNS(CP_NS, 'indicators')
            objective_indicator_id_nodes = objective_indicators_node[0].getElementsByTagNameNS(CP_NS, 'indicator_id')
            for current_objective_indicator_id_node in objective_indicator_id_nodes:
                if current_objective_indicator_id_node.hasChildNodes():
                    indicator_id = return_xml_data(current_objective_indicator_id_node)
                    indicator_ids.append(indicator_id)
            try:
                comment = return_xml_data(current_objective_node.getElementsByTagNameNS(CP_NS,'comment')[0])
            except:
                comment = u''
            percent_complete_nodes = current_objective_node.getElementsByTagNameNS(CP_NS, 'percent_complete_measure')
            percent_complete_measures = self.extract_percent_complete_info(percent_complete_nodes)

            objectives.append({ 'id':                           id,
                                'name':                         name,
                                'comment':                      comment,
                                'indicator_ids':                indicator_ids,
                                'percent_complete_measures':    percent_complete_measures,
                                })
        return objectives


    def extract_percent_complete_info (self,percent_complete_nodes):
        """
        Extract percent complete info from percent_complete_nodes. It contains 0 to many percent complete measures.
        """
        percent_complete_measures = []
        for percent_complete_node in percent_complete_nodes:
            sequence = percent_complete_node.getAttribute('sequence')
            try:
                measure = return_xml_data(percent_complete_node.getElementsByTagNameNS(CP_NS,'measure')[0])
                measure = str(Decimal(measure) / 100)     # Miradi stores the actual percentage value (0 - 100) whereas ConPro stores the fractional representation (0 - 1)
            except:
                measure = u''
            try:
                date = return_xml_data(percent_complete_node.getElementsByTagNameNS(CP_NS,'date')[0])
            except:
                date = u''
            try:
                comment = return_xml_data(percent_complete_node.getElementsByTagNameNS(CP_NS,'comment')[0])
            except:
                comment = u''

            percent_complete_measures.append({  'sequence':     sequence,
                                                'measure':      measure,
                                                'date':         fix_date(date),
                                                'comment':      comment,
                                                })
        return percent_complete_measures


    def extract_threat_info (self,threats_node):
        """Extract threat info from threats_node It contains 0 to many threats.
        """
        threats = []
        for threat_node in threats_node:
            threat_nodes = threat_node.getElementsByTagNameNS(CP_NS, 'threat')
            for current_threat_node in threat_nodes:
                id = current_threat_node.getAttribute('id')
                name = ' '.join(return_xml_data(current_threat_node.getElementsByTagNameNS(CP_NS,'name')[0]).split())
                try:
                    threat_taxonomy_code = return_xml_data(current_threat_node.getElementsByTagNameNS(CP_NS,'threat_taxonomy_code')[0])
                except:
                    threat_taxonomy_code = u''
                try:
                    threat_to_project_rank = return_xml_data(current_threat_node.getElementsByTagNameNS(CP_NS,'threat_to_project_rank')[0])
                except:
                    threat_to_project_rank = u''
                indicator_ids = []
                threat_indicators_node = current_threat_node.getElementsByTagNameNS(CP_NS, 'indicators')
                threat_indicator_id_nodes = threat_indicators_node[0].getElementsByTagNameNS(CP_NS, 'indicator_id')
                for current_threat_indicator_id_node in threat_indicator_id_nodes:
                    if current_threat_indicator_id_node.hasChildNodes():
                        indicator_id = return_xml_data(current_threat_indicator_id_node)
                        indicator_ids.append(indicator_id)

                threats.append({    'id':                       id,
                                    'name':                     name,
                                    'threat_taxonomy_code':     threat_taxonomy_code,
                                    'threat_to_project_rank':   threat_to_project_rank,
                                    'indicator_ids':            indicator_ids,
                                    })
        return threats


    def extract_target_info (self,targets_node):
        """Extract target info from target_node. target_node contains 0 to many
        targets.
        """

        targets = []
        viability_assessments = []
        keas = []
        target_id_2_row = {}
        kea_wid_generator = 1
        for target_node in targets_node:
            target_nodes = target_node.getElementsByTagNameNS(CP_NS, 'target')
            for current_target_node in target_nodes:
                target = {}

                target['id'] = current_target_node.getAttribute('id').strip()
                current_target_id = target['id']

                try:
                    target['sequence'] = current_target_node.getAttribute('sequence').strip()
                except:
                    target['sequence'] = current_target_id

                try:
                    target['name'] = ' '.join(return_xml_data(current_target_node.getElementsByTagNameNS(CP_NS,'name')[0]).split())
                except:
                    target['name'] = 'YUCK! This is an unnamed target!!!!!!!!!!!!!!!!'

                try:
                    target['description'] = return_xml_data(current_target_node.getElementsByTagNameNS(CP_NS,'description')[0])
                except:
                    target['description'] =  u''

                try:
                    target['description_comment'] = return_xml_data(current_target_node.getElementsByTagNameNS(CP_NS,'description_comment')[0])
                except:
                    target['description_comment'] =  u''

                try:
                    curr_node = current_target_node.getElementsByTagNameNS(CP_NS,'target_viability_rank')[0]
                    target['target_viability_rank'] = return_xml_data(curr_node)
                    try:
                        target['target_viability_mode'] = curr_node.getAttribute('target_viability_mode').strip()
                    except:
                        target['target_viability_mode'] =  u'kea'
                except:
                    target['target_viability_rank'] = u''
                    target['target_viability_mode'] =  u'kea'

                try:
                    target['target_viability_comment'] = return_xml_data(current_target_node.getElementsByTagNameNS(CP_NS,'target_viability_comment')[0])
                except:
                    target['target_viability_comment'] =  u''

                habitat_taxonomy_codes = []
                habitat_taxonomy_nodes = current_target_node.getElementsByTagNameNS(CP_NS,'habitat_taxonomy_code')
                for habitat_taxonomy_node in habitat_taxonomy_nodes:
                    habitat_taxonomy_codes.append(return_xml_data(habitat_taxonomy_node))
                target['habitat_taxonomy_codes'] = habitat_taxonomy_codes

                nested_targets = []
                nested_targets_nodes = current_target_node.getElementsByTagNameNS(CP_NS,'nested_target')
                for nested_target_node in nested_targets_nodes:
                    sequence = nested_target_node.getAttribute('sequence')
                    sequence = str(int(sequence)+1)
                    try:
                        name = ' '.join(return_xml_data(nested_target_node.getElementsByTagNameNS(CP_NS,'name')[0]).split())
                    except:
                        continue    # Skip past this nested_target if there is no name element.
                    try:
                        comment = return_xml_data(nested_target_node.getElementsByTagNameNS(CP_NS,'comment')[0])
                    except:
                        comment = u''
                    nested_targets.append({ 'sequence':sequence,
                                            'name':name,
                                            'comment':comment
                                            })
                target['nested_targets'] = nested_targets

                strategy_threat_target_assocs = []
                strategy_threat_target_assoc_nodes = current_target_node.getElementsByTagNameNS(CP_NS,'strategy_threat_target_association')
                try:
                    for strategy_threat_target_assoc_node in strategy_threat_target_assoc_nodes:
                        threat_id = return_xml_data(strategy_threat_target_assoc_node.getElementsByTagNameNS(CP_NS,'threat_id')[0])
                        strategy_id = return_xml_data(strategy_threat_target_assoc_node.getElementsByTagNameNS(CP_NS,'strategy_id')[0])
                        strategy_threat_target_assocs.append({  'threat_id':threat_id,
                                                                'strategy_id':strategy_id
                                                                })
                except:
                    pass #nothing to do
                target['strategy_threat_target_assocs'] = strategy_threat_target_assocs

                stresses = []
                stress_nodes = current_target_node.getElementsByTagNameNS(CP_NS,'stress')
                for stress_node in stress_nodes:
                    sequence = stress_node.getAttribute('sequence')
                    sequence = str(int(sequence)+1)
                    name = ' '.join(return_xml_data(stress_node.getElementsByTagNameNS(CP_NS,'name')[0]).split())
                    try:
                        severity = return_xml_data(stress_node.getElementsByTagNameNS(CP_NS,'stress_severity')[0])
                    except:
                        severity = u''
                    try:
                        scope = return_xml_data(stress_node.getElementsByTagNameNS(CP_NS,'stress_scope')[0])
                    except:
                        scope = u''
                    try:
                        to_target_rank = return_xml_data(stress_node.getElementsByTagNameNS(CP_NS,'stress_to_target_rank')[0])
                    except:
                        to_target_rank = u''

                    to_threat_ratings = []
                    to_threat_rating_nodes = stress_node.getElementsByTagNameNS(CP_NS,'threat_stress_rating')
                    for to_threat_rating_node in to_threat_rating_nodes:
                        threat_id = return_xml_data(to_threat_rating_node.getElementsByTagNameNS(CP_NS,'threat_id')[0])
                        try:
                            contrib_rank = return_xml_data(to_threat_rating_node.getElementsByTagNameNS(CP_NS,'contrib_rank')[0])
                        except:
                            contrib_rank = u''
                        try:
                            irreversible_rank = return_xml_data(to_threat_rating_node.getElementsByTagNameNS(CP_NS,'irreversible_rank')[0])
                        except:
                            irreversible_rank = u''
                        try:
                            stress_threat_to_target_rank = return_xml_data(to_threat_rating_node.getElementsByTagNameNS(CP_NS,'stress_threat_to_target_rank')[0])
                        except:
                            stress_threat_to_target_rank = u''

                        to_threat_ratings.append({  'target_wid':current_target_id, # This key is almost certainly extraneous - handled in convertMiradiToConProDataDict.py
                                                    'threat_id':threat_id,
                                                    'contrib_rank':contrib_rank,
                                                    'irreversible_rank':irreversible_rank,
                                                    'stress_threat_to_target_rank':stress_threat_to_target_rank,
                                                    })

                    stresses.append({   'sequence':sequence,
                                        'name':name,
                                        'severity':severity,
                                        'scope':scope,
                                        'to_target_rank':to_target_rank,
                                        'to_threat_ratings':to_threat_ratings
                                        })

                target['stresses'] = stresses

                threat_target_assocs = []
                threat_target_assoc_nodes = current_target_node.getElementsByTagNameNS(CP_NS,'threat_target_association')

                for threat_target_assoc_node in threat_target_assoc_nodes:
                    threat_id = return_xml_data(threat_target_assoc_node.getElementsByTagNameNS(CP_NS,'threat_id')[0])
                    try:
                        threat_to_target_rank = return_xml_data(threat_target_assoc_node.getElementsByTagNameNS(CP_NS,'threat_to_target_rank')[0])
                    except:
                        threat_to_target_rank = u''
                    try:
                        severity = return_xml_data(threat_target_assoc_node.getElementsByTagNameNS(CP_NS,'threat_severity')[0])
                    except:
                        severity = u''
                    try:
                        scope = return_xml_data(threat_target_assoc_node.getElementsByTagNameNS(CP_NS,'threat_scope')[0])
                    except:
                        scope = u''
                    try:
                        irreversibility = return_xml_data(threat_target_assoc_node.getElementsByTagNameNS(CP_NS,'threat_irreversibility')[0])
                    except:
                        irreversibility = u''
                    try:
                        comment = return_xml_data(threat_target_assoc_node.getElementsByTagNameNS(CP_NS,'threat_target_comment')[0])
                    except:
                        comment = u''
                    threat_target_assocs.append({   'threat_id':threat_id,
                                                    'threat_to_target_rank':threat_to_target_rank,
                                                    'severity':severity,
                                                    'scope':scope,
                                                    'irreversibility':irreversibility,
                                                    'comment':comment
                                                    })

                target['threat_target_assocs'] = threat_target_assocs
                targets.append(target)


                kea_nodes = current_target_node.getElementsByTagNameNS(CP_NS,'key_attribute')
                for kea_node in kea_nodes:
                    name = ' '.join(return_xml_data(kea_node.getElementsByTagNameNS(CP_NS,'name')[0]).split())
                    try:
                        category = return_xml_data(kea_node.getElementsByTagNameNS(CP_NS,'category')[0])
                    except:
                        category = u'Landscape Context'
                    keas.append({   'id':kea_wid_generator,
                                    'name':name,
                                    'category':category,
                                    })

                    viability_assessment_nodes = kea_node.getElementsByTagNameNS(CP_NS, 'viability_assessment')
                    for current_viability_assessment_node in viability_assessment_nodes:
                        indicator_id = return_xml_data(current_viability_assessment_node.getElementsByTagNameNS(CP_NS,'indicator_id')[0])
                        try:
                            indicator_description_poor = return_xml_data(current_viability_assessment_node.getElementsByTagNameNS(CP_NS,'indicator_description_poor')[0])
                        except:
                            indicator_description_poor = u''
                        try:
                            indicator_description_fair = return_xml_data(current_viability_assessment_node.getElementsByTagNameNS(CP_NS,'indicator_description_fair')[0])
                        except:
                            indicator_description_fair = u''
                        try:
                            indicator_description_good = return_xml_data(current_viability_assessment_node.getElementsByTagNameNS(CP_NS,'indicator_description_good')[0])
                        except:
                            indicator_description_good = u''
                        try:
                            indicator_description_very_good = return_xml_data(current_viability_assessment_node.getElementsByTagNameNS(CP_NS,'indicator_description_very_good')[0])
                        except:
                            indicator_description_very_good = u''
                        try:
                            source_indicator_ratings = return_xml_data(current_viability_assessment_node.getElementsByTagNameNS(CP_NS,'source_indicator_ratings')[0])
                        except:
                            source_indicator_ratings = u''
                        try:
                            desired_viability_rating = return_xml_data(current_viability_assessment_node.getElementsByTagNameNS(CP_NS,'desired_viability_rating')[0])
                        except:
                            desired_viability_rating = u''
                        try:
                            desired_rating_date = return_xml_data(current_viability_assessment_node.getElementsByTagNameNS(CP_NS,'desired_rating_date')[0])
                        except:
                            desired_rating_date = u''
                        try:
                            kea_and_indicator_comment = return_xml_data(current_viability_assessment_node.getElementsByTagNameNS(CP_NS,'kea_and_indicator_comment')[0])
                        except:
                            kea_and_indicator_comment = u''
                        try:
                            indicator_rating_comment = return_xml_data(current_viability_assessment_node.getElementsByTagNameNS(CP_NS,'indicator_rating_comment')[0])
                        except:
                            indicator_rating_comment = u''
                        try:
                            desired_rating_comment = return_xml_data(current_viability_assessment_node.getElementsByTagNameNS(CP_NS,'desired_rating_comment')[0])
                        except:
                            desired_rating_comment = u''

                        viability_assessments.append({  'target_id':current_target_id,
                                                        'indicator_id':indicator_id,
                                                        'kea_id':kea_wid_generator,
                                                        'indicator_description_poor':indicator_description_poor,
                                                        'indicator_description_fair':indicator_description_fair,
                                                        'indicator_description_good':indicator_description_good,
                                                        'indicator_description_very_good':indicator_description_very_good,
                                                        'source_indicator_ratings':source_indicator_ratings,
                                                        'desired_viability_rating':desired_viability_rating,
                                                        'desired_rating_date':fix_date(desired_rating_date),
                                                        'kea_and_indicator_comment':kea_and_indicator_comment,
                                                        'indicator_rating_comment':indicator_rating_comment,
                                                        'desired_rating_comment':desired_rating_comment,
                                                        })

                    kea_wid_generator += 1
        return (targets,keas,viability_assessments)


    def build_full_team_compliment_string(self,persons):
        '''Return string with full team compliment from person nodes.'''
        # From Dan Salzer:
        # ConPro should concatenate the information on all team members from the XML into 
        # the existing Team text field in ConPro and display this in the Team Info section 
        # of ConPro. I recommend the following display format for each person by line:
        #           Full Name, Organization, Role
        ITEM_IN_ROW_DELIMETER = '; '
        rows = []
        for person in persons:
            persons_dict = []
            try:
                givenname = return_xml_data(person.getElementsByTagNameNS(CP_NS,'givenname')[0])
            except:
                givenname = u''
            try:
                surname = return_xml_data(person.getElementsByTagNameNS(CP_NS,'surname')[0])
            except:
                surname = u''
            # Was full name provided?
            if givenname and surname:
                persons_dict.append("%s %s" % (givenname,surname))
            else:
                continue # if no name was provided, no need to continue with current iteration for loop
            try:
                persons_dict.append(return_xml_data(person.getElementsByTagNameNS(CP_NS,'organization')[0]))
            except:
                pass
            try:
                role_nodes = person.getElementsByTagNameNS(CP_NS,'role')
            except:
                role_nodes = []
            roles = []
            for role_node in role_nodes:
                try:
                    roles.append(return_xml_data(role_node))
                except:
                    pass
            try:
                persons_dict.append(', '.join(roles))
            except:
                pass
            rows.append(ITEM_IN_ROW_DELIMETER.join(persons_dict))
        return '\n'.join(rows)


    def extract_project_team_info(self,persons):
        ''''''
        # From Dan Salzer:
        # If no team members are listed as Team Contact, Team Leader/Manager, or Process Facilitator, (in that order)
        # use the first person on the list for Contact Name, email, and organizational information.

        # Get all data into 'persons_dict' from which to pick the right contact info
        persons_dict = []

        for person in persons:
            # Get role info... Person can have 1 to many roles
            try:
                role_nodes = person.getElementsByTagNameNS(CP_NS,'role')
            except:
                role_nodes = []
            roles = []
            for role_node in role_nodes:
                try:
                    roles.append(return_xml_data(role_node))
                except:
                    pass
            try:
                givenname = return_xml_data(person.getElementsByTagNameNS(CP_NS,'givenname')[0])
            except:
                givenname = u''
            try:
                surname = return_xml_data(person.getElementsByTagNameNS(CP_NS,'surname')[0])
            except:
                surname = u''
            try:
                email = return_xml_data(person.getElementsByTagNameNS(CP_NS,'email')[0])
            except:
                email = u''
            try:
                phone = return_xml_data(person.getElementsByTagNameNS(CP_NS,'phone')[0])
            except:
                phone = u''
            try:
                organization = return_xml_data(person.getElementsByTagNameNS(CP_NS,'organization')[0])
            except:
                organization = u''
            persons_dict.append((', '.join(roles),givenname,surname,email,phone,organization))

        # Now pick the right person to return to ConPro
        return_this_person = []
        if len(persons_dict) == 1:
            return persons_dict[0]
        else:
            one_complete_contact = []
            for role in CONTACT_PERSON_ROLES_ORDERED:
                for person in persons_dict:
                    if not one_complete_contact:
                        if person[1] and person[2] and person[3]:
                            one_complete_contact = person
                    if role in person[0]:
                        if person[1] and person[2] and person[3]:
                            return person


        return one_complete_contact

    def extract_project_info(self,project_info_node):
        """Extract project info from project_info_node. project_info_node is a
        singleton.
        """
        for node in project_info_node:
            shareable = node.getAttribute('share_outside_organization')

            project_id = u''
            project_id_nodes = node.getElementsByTagNameNS(CP_NS,'project_id')
            for project_id_node in project_id_nodes:
                project_id = return_xml_data(project_id_node)
                project_id_context = project_id_node.getAttribute('context')
                if project_id_context.lower() == 'conpro':
                    project_id = project_id
                else:
                    project_id = u''

            try:
                project_name = ' '.join(return_xml_data(node.getElementsByTagNameNS(CP_NS,'name')[0]).split())
            except IndexError:
                project_name = ''

            try:
                start_date = return_xml_data(node.getElementsByTagNameNS(CP_NS,'start_date')[0])
            except IndexError:
                start_date = ''

            try:
                data_effective_date = return_xml_data(node.getElementsByTagNameNS(CP_NS,'data_effective_date')[0])
            except IndexError:
                data_effective_date = ''

            area_size_nodes = node.getElementsByTagNameNS(CP_NS,'area_size')
            acceptable_area_size = ''
            for area_size_node in area_size_nodes:
                area_size = return_xml_data(area_size_node)
                area_size_unit = area_size_node.getAttribute('unit')
                if area_size_unit == 'hectares':
                    acceptable_area_size = area_size

            latitude  = u''
            longitude = u''
            geo_spatial_nodes = node.getElementsByTagNameNS(CP_NS,'geospatial_location')
            for geo_spatial_node in geo_spatial_nodes:
                geo_spatial_type = geo_spatial_node.getAttribute('type')
                if geo_spatial_type == 'point':
                    try:
                        latitude  = return_xml_data(geo_spatial_node.getElementsByTagNameNS(CP_NS,'latitude')[0])
                    except IndexError:
                        latitude  = u''
                    try:
                        longitude = return_xml_data(geo_spatial_node.getElementsByTagNameNS(CP_NS,'longitude')[0])
                    except IndexError:
                        longitude = u''
            try:
                description_comment = return_xml_data(node.getElementsByTagNameNS(CP_NS,'description_comment')[0])
            except IndexError:
                description_comment = u''

            try:
                goal_comment = return_xml_data(node.getElementsByTagNameNS(CP_NS,'goal_comment')[0])
            except IndexError:
                goal_comment = u''

            persons = node.getElementsByTagNameNS(CP_NS,'person')
            try:
                planning_team_comment = return_xml_data(node.getElementsByTagNameNS(CP_NS,'planning_team_comment')[0])
            except IndexError:
                planning_team_comment = u''
            # Get full team compliment and append to planning_team_comment.  ConPro only supports one contact so we import the
            # whole project team from Miradi into the planning_team_comment.
            full_team_string = self.build_full_team_compliment_string(persons)
            planning_team_comment = """%s

Full Team Compliment:
%s""" % (planning_team_comment,full_team_string)
            planning_team_comment = planning_team_comment.strip()

            try:
                lessons_learned = return_xml_data(node.getElementsByTagNameNS(CP_NS,'lessons_learned')[0])
            except IndexError:
                lessons_learned = u''

            try:
                related_projects = return_xml_data(node.getElementsByTagNameNS(CP_NS,'related_projects')[0])
            except IndexError:
                related_projects = u''

            try:
                stressless_threat_rank = return_xml_data(node.getElementsByTagNameNS(CP_NS,'stressless_threat_rank')[0])
                # This is per email from David Berg dated Nov 12, 2008 "Stressless Threat Rank"
                if stressless_threat_rank:
                    stressless_threat_rank = u'1'
                else:
                    stressless_threat_rank = u'0'
            except IndexError:
                stressless_threat_rank = u'0'

            try:
                project_threat_rank = return_xml_data(node.getElementsByTagNameNS(CP_NS,'project_threat_rank')[0])
            except IndexError:
                project_threat_rank = u''

            try:
                project_viability_rank = return_xml_data(node.getElementsByTagNameNS(CP_NS,'project_viability_rank')[0])
            except IndexError:
                project_viability_rank = u''

            try:
                role,givenname,surname,email,phone,organization = self.extract_project_team_info(persons)
            except IndexError:
                role = givenname = surname = email = phone = organization = ''
            except ValueError:
                role = givenname = surname = email = phone = organization = ''

            ecoregion_codes = []
            ecoregion_nodes = node.getElementsByTagNameNS(CP_NS,'ecoregion_code')
            for ecoregion_node in ecoregion_nodes:
                try:
                    ecoregion_codes.append(return_xml_data(ecoregion_node))
                except AttributeError:
                    pass

            country_codes = []
            country_nodes = node.getElementsByTagNameNS(CP_NS,'country_code')
            for country_node in country_nodes:
                try:
                    country_codes.append(return_xml_data(country_node))
                except AttributeError:
                    pass

            ou_codes = []
            ou_nodes = node.getElementsByTagNameNS(CP_NS,'ou_code')
            for ou_node in ou_nodes:
                try:
                    ou_codes.append(return_xml_data(ou_node))
                except AttributeError:
                    pass
                    
            classification_ids = []
            classification_nodes = node.getElementsByTagNameNS(CP_NS,'classification_id')
            for classification_node in classification_nodes:
                try:
                    classification_ids.append(return_xml_data(classification_node))
                except AttributeError:
                    pass

            try:
                exporter_name = return_xml_data(node.getElementsByTagNameNS(CP_NS,'exporter_name')[0])
            except IndexError:
                exporter_name = ''

            try:
                exporter_version = return_xml_data(node.getElementsByTagNameNS(CP_NS,'exporter_version')[0])
            except IndexError:
                exporter_version = ''

            try:
                export_date = return_xml_data(node.getElementsByTagNameNS(CP_NS,'export_date')[0])
            except IndexError:
                export_date = ''

            return  [{  'project_id':               project_id,
                        'shareable':                shareable,
                        'project_name':             project_name,
                        'start_date':               fix_date(start_date),
                        'data_effective_date':      fix_date(data_effective_date),
                        'latitude':                 latitude,
                        'longitude':                longitude,
                        'givenname':                givenname,
                        'surname':                  surname,
                        'email':                    email,
                        'phone':                    phone,
                        'organization':             organization,
                        'ecoregion_codes':          ecoregion_codes,
                        'country_codes':            country_codes,
                        'ou_codes':                 ou_codes,
                        'classification_ids':       classification_ids,
                        'description_comment':      description_comment,
                        'goal_comment':             goal_comment,
                        'hectares':                 acceptable_area_size,
                        'planning_team_comment':    planning_team_comment,
                        'lessons_learned':          lessons_learned,
                        'related_projects':         related_projects,
                        'stressless_threat_rank':   stressless_threat_rank,
                        'project_threat_rank':      project_threat_rank,
                        'project_viability_rank':   project_viability_rank,
                        'exporter_name':            exporter_name,
                        'exporter_version':         exporter_version,
                        'export_date':              fix_date(export_date),
                        }]


    def tear_down_project_xml_doc(self,xmldoc_str):
        """
        Accept an XML file or file-like object. Parse the XML and get the data
        to populate the database.
        """

        from xml.dom import minidom
        dom = minidom.parseString(xmldoc_str)


        #Get the individual pieces which make up a conservation project
        xml_obj_project_summary = dom.getElementsByTagNameNS(CP_NS, 'project_summary')
        self.cp_data_structure['project_info'] = self.extract_project_info(xml_obj_project_summary)

        xml_obj_targets = dom.getElementsByTagNameNS(CP_NS, 'targets')
        self.cp_data_structure['targets'],self.cp_data_structure['keas'],self.cp_data_structure['viability_assessments'] = self.extract_target_info(xml_obj_targets)

        xml_obj_threats = dom.getElementsByTagNameNS(CP_NS, 'threats')
        self.cp_data_structure['threats'] = self.extract_threat_info(xml_obj_threats)

        xml_obj_objectives = dom.getElementsByTagNameNS(CP_NS, 'objective')
        self.cp_data_structure['objectives'] = self.extract_objective_info(xml_obj_objectives)

        xml_obj_strategies = dom.getElementsByTagNameNS(CP_NS, 'strategies')
        self.cp_data_structure['strategies'] = self.extract_strategy_info(xml_obj_strategies)

        xml_obj_indicators = dom.getElementsByTagNameNS(CP_NS, 'indicator')
        self.cp_data_structure['indicators'] = self.extract_indicator_info(xml_obj_indicators)

        xml_obj_methods = dom.getElementsByTagNameNS(CP_NS, 'method')
        self.cp_data_structure['methods'] = self.extract_method_info(xml_obj_methods)


    def get_xml_tear_down_data(self,xmldoc_str):
        self.tear_down_project_xml_doc(xmldoc_str)
        return self.cp_data_structure


def return_xml_data(data_node):
    try:
        return data_node.firstChild.data.strip()
    except:
        return u''


def convert_to_unicode(value):
    # We want all data to be unicode for processing
    if isinstance(value,str):
        # If this is a string, convert to unicode. Decode using utf-8 and if not successful,
        # decode using Windows-1252. If that fails, too, then decode using Windows-1252 with
        # replace option.
        try:
            value = value.decode("utf-8",'strict')
        except UnicodeDecodeError:
            value = value.decode("Windows-1252",'replace')

    # Node contents must be a string, so make everything a string
    if not isinstance(value,unicode):
        try:
            value = u"%s" % value
        except UnicodeDecodeError:
            value = u''

    return value


def fix_date(dateObj):
    dateStr = '%s' % dateObj
    return dateStr.replace('/','-')


if __name__ == '__main__':
    td = XmlTearDown()
    data = open("DenseProject.xml").read()
    test = td.get_xml_tear_down_data(data)
	

